/*
 * Copyright 2022 The Open Islands Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openislands.oi.config.argumentresolver;

import com.alibaba.fastjson.JSONObject;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

public class HttpRequestHeaderResolver extends AbstractNamedValueMethodArgumentResolver {
    /***
     * {@link HttpRequestHeader}
     * @param parameter the method parameter to check
     * @return {@code true} if this resolver supports the supplied parameter;
     * {@code false} otherwise
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(HttpRequestHeader.class);
    }

    @Override
    protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
        HttpRequestHeader hrs = parameter.getParameterAnnotation(HttpRequestHeader.class);
        Assert.state(hrs != null, "No HttpRequestHeader annotation");
        return new NamedValueInfo(hrs.value(), hrs.required(), "");
    }

    @Override
    @Nullable
    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) {
        final Class<?> parameterType = parameter.getParameterType();
        if (!name.isEmpty() && parameterType == String.class) {
            String[] headerValues = request.getHeaderValues(name);
            return (headerValues != null && headerValues.length >= 1 ? headerValues[0] : null);
        } else {
            final Iterator<String> headerNames = request.getHeaderNames();
            if (parameterType == Map.class) {
                Map<String, String> headers = new HashMap<>();
                while (headerNames.hasNext()) {
                    final String next = headerNames.next();
                    headers.put(next, request.getHeader(next));
                }
                return headers;
            } else {
                final JSONObject fieldValues = new JSONObject();
                Field[] fields = parameterType.getDeclaredFields();
                for (Field field : fields) {
                    HeaderVariable headerVariable = field.getAnnotation(HeaderVariable.class);
                    if (headerVariable != null) {
                        String value = headerVariable.value();
                        if (value.trim().length() > 0){
                            String header = request.getHeader(value);
                            if (Objects.isNull(header) && headerVariable.required()){
                                throw new IllegalArgumentException("Http header " + value + " is required");
                            }else {
                                fieldValues.put(field.getName(), header);
                            }
                        }
                    }
                }
                return fieldValues.toJavaObject(parameterType);
            }
        }
    }
}
